﻿#region Disclaimer/Info
///////////////////////////////////////////////////////////////////////////////////////////////////
// MetaCollection
// 
// MetaCollection is a system to provide a standard structure, permissions and metadata service
// for content organization.
//
// This project is hosted at Google Code at http://code.google.com/p/metacollection/
//
// This project is licensed under the BSD license.  See the License.txt file for more information.
///////////////////////////////////////////////////////////////////////////////////////////////////
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MetaCollection.Framework;
using MetaCollection.Framework.Model;
using System.Security;

namespace MetaCollection.ContentService.WebDav.Methods
{
	/// <summary>
	/// Provides support for the MOVE http verb on items in the collection.
	/// </summary>
	public static class Move
	{
		/// <summary>
		/// Processes the MOVE request on an item.
		/// </summary>
		/// <param name="context">The context.</param>
		public static void Respond(DavContext context, IRequestHandler handler)
		{
			DepthOption depth = context.Request.Depth();
			Uri destination = context.Request.Destination;
			IItemInstance item = context.Item as IItemInstance;
			string targetItemName = destination.Segments.Last();

			if (item == null)
				throw new Http.ClientException(Http.ClientErrorStatus.NotFound);

			if ((depth != DepthOption.Undefined) && (depth != DepthOption.AllDescendants))
				throw new Http.ClientException(Http.ClientErrorStatus.BadRequest, "Invalid Depth Option.  Only Depth of infinity may be specified if given.");

			// load up new parent.. take id and set item.parentid to it.
			Uri parentUri = new Uri(destination.AbsoluteUri.Substring(0, destination.AbsoluteUri.LastIndexOf('/')));
			var destinationItem = handler.ItemRequest(context.User, RequestTypes.GetItem, destination, LoadOptions.ItemReference | LoadOptions.Href).FirstOrDefault();
			IItemInstance parentItem = handler.ItemRequest(context.User, RequestTypes.GetItem, parentUri, LoadOptions.ItemReference | LoadOptions.Href).FirstOrDefault();

			// if the parent does not exist then fail with a 409 Conflict.
			if (parentItem == null)
				throw new Http.ClientException(Http.ClientErrorStatus.Conflict, "Destination Folder Must Exist");

			if (parentItem.Href.AbsoluteUri.StartsWith(item.Href.AbsoluteUri))
				throw new Http.ClientException(Http.ClientErrorStatus.Conflict, "Destination item cannot be a child of the source item.");

			// TODO : if exists then check overwrite, if F then fail bad request.. If T then fail not supported/implemented.
			if (destinationItem != null) // && Header.Overwrite == false)
				throw new Http.ClientException(Http.ClientErrorStatus.Conflict, "Source item name conflicts with the name of an item in the destination folder.");

			item.ParentId = parentItem.Id;
			// see if a rename is occuring... if it is then we need to lock/version
			if (!item.Name.Equals(targetItemName, StringComparison.OrdinalIgnoreCase))
			{
				ProcessLocks(item, handler);
				item.Name = targetItemName;
			}
			handler.ItemRequest(context.User, RequestTypes.SetItem, item.Href, item);
			context.Response.SetStatus((int)Http.SuccessStatus.Ok, "Ok");
		}

		/// <summary>
		/// Processes the locks on the item.  If the current user does not own the lock
		/// (by user or because it is published) then a new lock/version is created.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <param name="handler">The handler.</param>
		static void ProcessLocks(IItemInstance item, IRequestHandler handler)
		{
			// If the item is currently published or the user does not own a lock on the item then we need to make a new version
			if (!LockProperty.CurrentUserOwnsLock(item) || item.Published)
			{
				item.LeftVersion = item.Version;
				item.Version = 0;
				// TODO: there appears to be a path resolution issue even with version specific requests... FindByName needs to have a version option.
				// if the item name of a version does not match the published version it will not resolve (404).
				//				item.Published = false;
			}
			// update the lock on the item so our timeout is moved out into the future
			item.Properties = item.Properties.PatchProperty(Properties.CONTENTSERVICE_LOCK_PROPERTY_NAME, LockProperty.GetNewLockForCurrentUser()); // lock the item to the user.
		}

		///// <summary>
		///// Determines whether the item referenced by itemUri [has in bound relationships].
		///// </summary>
		///// <remarks>This method depends on the functionality of the search method being that the
		///// target item match must be a like (startswith)</remarks>
		///// <param name="itemUri">The item URI.</param>
		///// <param name="handler">The handler.</param>
		///// <returns>
		///// 	<c>true</c> if the specifid item [has in bound relationships] otherwise, <c>false</c>.
		///// </returns>
		//static bool HasInBoundRelationships(Uri itemUri, IRequestHandler handler)
		//{
		//    ItemInstance searchItem = new ItemInstance();
		//    searchItem.Relationships = new IItemRelationship[] { new ItemRelationship { TargetItem = itemUri } };
		//    IEnumerable<IItemInstance> relatedItems = handler.ItemRequest(RequestTypes.GetItemQuery, itemUri, searchItem, LoadOptions.ItemReference);
		//    return (!(relatedItems == null || relatedItems.FirstOrDefault() == null));
		//}
	}
}
